package com.topwalk.datacollect.server;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.topwalk.datacollect.config.ApplicationConfig;
import com.topwalk.datacollect.config.HeaderConfig;
import com.topwalk.datacollect.util.DateUtil;

/**
 * syslog日志类.
 * 
 * @author shijing
 */
public class SysLogCollectionServer {

	private static final Logger LOG = LoggerFactory.getLogger(SysLogCollectionServer.class);
	
	private static long IN_NUM = 0;
	private static long OUT_NUM = 0;
	
	public static final Map<String, String[]> CSV_MAP = new HashMap<String, String[]>();
	
	public static void readCsv(String csvName){
		InputStream io = SysLogCollectionServer.class.getResourceAsStream("/csv/"+csvName);
		BufferedReader br = null;
		try {
			List<String> strlist = new ArrayList<String>();
			br = new BufferedReader(new InputStreamReader(io));
			String str = "";
			while((str = br.readLine()) != null){
				strlist.add(str);
			}
			SysLogCollectionServer.CSV_MAP.put(csvName, strlist.toArray(new String[strlist.size()]));
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(br != null){
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void getSysLog() throws Exception {
		
		// 发送数据相关配置
		ObjectMapper om = new ObjectMapper();
		Encoder encoder = Base64.getEncoder();
		// 获取socket连接
		String ip = ApplicationConfig.INC_SOCKET_IP;
		int port = ApplicationConfig.INC_SOCKET_PORT;
		Socket socket = new Socket(ip, port);
		DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
		// 启动syslog服务
		String serverHost = "0.0.0.0";
		int serverPort = ApplicationConfig.SYSLOG_PORT;
		UDPServerSocket udpServerSocket = null;
		udpServerSocket = new UDPServerSocket(serverHost, serverPort);
		LOG.info("--------------UDP服务端启动成功，等待接收消息...----------------");
		while (true) {
			String header = new String(HeaderConfig.INC);
			String sysLogData = "";
			try {
				sysLogData = udpServerSocket.receive();
				LOG.info("接收syslog："+(++IN_NUM)+"条："+sysLogData);
			} catch (IOException e) {
				LOG.error("接收syslog数据失败！");
				e.printStackTrace();
				udpServerSocket.close();
				try {
					TimeUnit.SECONDS.sleep(1);
					udpServerSocket = new UDPServerSocket(serverHost, serverPort);
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
			try {
				boolean normal = false;
				// 按逗号分割（双引号内的逗号除外）
				String[] arraySysLogData = sysLogData.split(",(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)", -1);
				int actual_length = arraySysLogData.length;
				if (actual_length > 2) {
					String tableName = null;
					String logID = arraySysLogData[2].trim();
					int lastIndexOf = logID.lastIndexOf("_");
					if(lastIndexOf != -1){
						String logID_dataType = logID.substring(0,lastIndexOf).substring(1).toLowerCase();
						if(CSV_MAP.containsKey(logID_dataType)){
							tableName = logID_dataType;
						}
					}
					// 检查表名
					if (tableName != null) {
						String[] fields = CSV_MAP.get(tableName);
						int expect_length = fields.length;
						// 检查字段个数
						if (actual_length == expect_length) {
							// 数据正常
							normal = true;
							// csv转map
							Map<String, Object> map = new HashMap<String, Object>();
							// 取第一个数据，因为syslog格式有时间和主机名称，把这些信息去掉(<8>Dec 18 10:37:52 DESKTOP-56C89GT)
							String firstData = arraySysLogData[0].trim();
							firstData = firstData.substring(firstData.lastIndexOf(" ")+1);
							// 去掉引号
							firstData = symbolTrim(firstData);
							map.put(fields[0], firstData);
							for (int i = 1; i < expect_length; i++) {
								String data = arraySysLogData[i].trim();
								// 去掉引号
								data = symbolTrim(data);
								map.put(fields[i], data);
							}
							// 设置头部dataType
							header = header.replace("$dataType", tableName);
							// 设置头部logTime
							String time = map.get("logTime").toString();
							String data = header.replace("$logTime", time);
							// 添加统一字段
							map.put("dataType", tableName);// 数据类型
							map.put("ZJLID", "");// 主记录ID（空）
							map.put("SJLYXTFLDM", ApplicationConfig.addData_SJLYXTFLDM);// 数据来源系统分类代码
							map.put("CJD", ApplicationConfig.addData_SJLYXTFLDM);// 采集地
							map.put("XXRKSJ", "");// 信息入库时间（空）
							map.put("SJMGJB", "");// 数据敏感级别（空）
							// map转json
							String json = om.writeValueAsString(map);
							// json转base64
							String base64 = encoder.encodeToString(json.getBytes("UTF-8"));
							// 添加businessData
							data = data.replace("$businessData", base64);
							// 发送数据
							dos.write((data+"\n").getBytes("UTF-8"));
							LOG.info("发送syslog："+(++OUT_NUM)+"条："+data);
						}
					}
				}
				// 非规范的syslog日志
				if(!normal){
					// 设置头部dataType
					header = header.replace("$dataType", "source_unmsyslog_log");
					// 设置头部logTime
					String data = header.replace("$logTime", DateUtil.getCurrentTime("yyyyMMddHHmmss"));
					// 内容
					Map<String, Object> map = new HashMap<String, Object>();
					map.put("message", sysLogData);
					String json = om.writeValueAsString(map);
					// 转base64
					String base64 = encoder.encodeToString(json.getBytes("UTF-8"));
					// 添加businessData
					data = data.replace("$businessData", base64);
					// 发送数据
					dos.write((data+"\n").getBytes("UTF-8"));
					LOG.info("发送syslog："+(++OUT_NUM)+"条："+data);
				}
			}catch (IOException e){
				LOG.error("发送数据失败！重新建立socket连接...");
				e.printStackTrace();
				try {
					socket.close();
					socket = new Socket(ip, port);
					TimeUnit.SECONDS.sleep(1);
					dos = new DataOutputStream(socket.getOutputStream());
				} catch (Exception e1) {
					LOG.error("重新建立socket连接失败！");
					e1.printStackTrace();
				}
			}catch (Exception e) {
				LOG.error("数据处理异常！");
				e.printStackTrace();
			}
		}

	}
	
	public static String symbolTrim(String str){
		String result = str;
		if (str.startsWith("\"") && str.endsWith("\"")) {
			result = str.substring(1, str.length() - 1);
		}
		return result;
	}
	
}
